(function(angular) {
  'use strict';
  angular
    .module('fs.router', ['fs.logger', 'fs.storage', 'ui.router', 'oc.lazyLoad'])
    .provider('routerService', routerService)
    .config(configuration)
    .run(main);
  routerService.$inject = ['$stateProvider', '$urlRouterProvider', '$locationProvider'];
  function routerService($stateProvider, $urlRouterProvider, $locationProvider) {
    var provider = this;
    provider.theme = 'default';
    provider.multipleViews = {};
    provider.cacheLayout = false;
    provider.cacheResource = false;
    provider.cacheAuthorization = false;
    provider.cacheValidationRule = false;
    provider.lazyLoad = lazyLoad;
    provider.preLoadLayoutConfig = preLoadLayoutConfig;
    provider.preLoadValidatorConfig = preLoadValidatorConfig;
    provider.preLoadI18nConfig = preLoadI18nConfig;
    provider.preLoadAuthorizationConfig = preLoadAuthorizationConfig;
    provider.setTheme = function(theme) {
      provider.theme = theme;
    };
    provider.getTheme = function() {
      return provider.theme;
    };
    provider.setHomeUrl = function(homeUrl) {
      $urlRouterProvider.otherwise(homeUrl);
    };
    provider.setStates = function(stateConfig) {
      angular.forEach(
        stateConfig,
        function(value, key) {
          if (value.views) {
            provider.multipleViews[key.split('.').join('_')] = key;
          }
          this.state(key, value);
        },
        $stateProvider
      );
    };
    provider.getTemplateUrl = function(templateUrl) {
      return templateUrl.replace('${theme}', provider.theme);
    };
    provider.setSimpleStates = function(simpleStates, module) {
      var states = {};
      simpleStates = flattenSimpleStateConfig(simpleStates);
      angular.forEach(simpleStates, function(value, key) {
        if (key.indexOf('^') == -1) {
          simpleStates[module + '_' + key] = value;
          delete simpleStates[key];
        } else {
          simpleStates[key.substr(1)] = value;
          delete simpleStates[key];
        }
      });
      angular.forEach(
        simpleStates,
        function(stateConfig, stateName) {
          stateConfig = stateConfig || {};
          if (!this[stateName] && !stateConfig.__ignore) {
            stateConfig.module = module;
            this[stateName] = buildState(stateName.split('.').join('_'), stateConfig);
          }
        },
        states
      );
      try {
        provider.setStates(states);
      } catch (e) {
        provider.log.debug(e);
      }
    };

    getter.$inject = ['storageService', 'StorageConstants', '$log'];
    function getter(storageService, StorageConstants, $log) {
      provider.storageService = storageService;
      provider.Constants = StorageConstants;
      provider.log = $log.getInstance('routerService');
      return {
        setTheme: provider.setTheme,
        getTheme: provider.getTheme,
        parseState: parseState
      };
    }
    provider.$get = getter;

    function parseState(state) {
      if (state.charAt(0) == '^') {
        state = state.replace('^', '');
      } else if (provider.multipleViews[state.split('.').join('_')]) {
        state = provider.multipleViews[state.split('.').join('_')];
      } else {
        state = state.split('.').join('_');
      }
      // TODO why use 'poi' ?
      if (state.indexOf('poi_') != 0) {
        state = 'poi_' + state;
      }
      return state;
    }
    function lazyLoad(files) {
      angular.forEach(
        files,
        function(file, index) {
          this[index] = file.replace('${theme}', provider.theme);
        },
        files
      );
      return [
        '$ocLazyLoad',
        '$stateParams',
        function($ocLazyLoad, $stateParams) {
          return $ocLazyLoad.load(files, {
            cache: true
          });
        }
      ];
    }
    function buildState(stateName, stateConfig, isMultipleViews) {
      var state = {
        url: '/' + (stateConfig.views ? stateName.split('_').pop() : stateName.split('_').join('/')),
        resolve: getResolve(stateName, stateConfig),
        params: {
          params: stateConfig.params
        }
      };
      state.url = state.url.replace(stateConfig.module + '/', '');
      if (isMultipleViews) {
        state.controller = stateConfig.controller || getController(stateName, stateConfig.__inheritController);
        state.controllerAs = stateConfig.controllerAs || 'vm';
      } else {
        state.controllerProvider = stateConfig.controllerProvider || getControllerProvider(stateName, stateConfig.__inheritController);
      }
      if (stateConfig.template) {
        state.template = stateConfig.template;
      } else {
        state.templateUrl = getTemplateUrl(stateName, stateConfig.templateUrl, stateConfig.module);
      }
      if (stateConfig.views) {
        state.views = {};
        angular.forEach(stateConfig.views, function(view, key) {
          state.views[key] = buildState(stateName, view, true);
          delete state.views[key].url;
          delete state.views[key].params;
          delete state.views[key].resolve;
        });
      }
      return state;
    }
    function getResolve(stateName, stateConfig) {
      return {
        dependentFile: preLoadDependFile(stateName, stateConfig.__inheritController, stateConfig.__dependentFiles, stateConfig.module),
        layoutConfig: preLoadLayoutConfig(stateConfig.__layoutConfig, stateName, stateConfig.module),
        validatorConfig: preLoadValidatorConfig(stateConfig.__validatorConfig, stateName, stateConfig.module),
        i18nConfig: preLoadI18nConfig(stateConfig.__i18nConfig, stateName, stateConfig.module),
        authService: preLoadAuthorizationConfig(stateConfig.__authorizationConfig, stateName, stateConfig.module)
      };
    }
    function getTemplateUrl(stateName, templateUrl, module) {
      if (templateUrl) {
        return (templateUrl = templateUrl.replace('${theme}', provider.theme));
      }
      return function($stateParams) {
        var fileName, prefix, path;
        path = stateName.split('_');
        if (path.length > 2) {
          fileName = path.pop();
          prefix = path.pop();
          fileName = prefix + fileName.charAt(0).toUpperCase() + fileName.slice(1);
          path.push(prefix);
          path.push(fileName);
        }
        if (path.length > 1) path.shift();
        templateUrl = 'resources/' + provider.theme + '/nux/view/' + path.join('/') + '.html';
        return templateUrl;
      };
    }
    function getController(stateName, inheritController) {
      var controller, stateNameArray;
      stateNameArray = stateName.split('_');
      if (inheritController === true) {
        stateNameArray.pop();
      }
      controller = stateNameArray.join('.') + 'Controller';
      return controller;
    }
    function getControllerProvider(stateName, inheritController) {
      controllerProvider.$inject = ['$state', '$stateParams'];
      function controllerProvider($state, $stateParams) {
        var controller,
          stateNameArray = stateName.split('_');
        if (inheritController === true) {
          stateNameArray.pop();
        }
        controller = stateNameArray.join('.') + 'Controller as vm';
        return controller;
      }
      return controllerProvider;
    }
    function preLoadDependFile(stateName, inheritController, files, module) {
      dependentFile.$inject = ['$ocLazyLoad', '$q', '$state', '$log'];
      function dependentFile($ocLazyLoad, $q, $state, $log) {
        var filePathArray,
          fileName,
          prefix,
          defer = $q.defer();
        var dependentFiles = angular.copy(files) || [];
        angular.forEach(dependentFiles, function(filePath, index) {
          dependentFiles[index] = filePath.replace('${theme}', provider.theme);
        });
        filePathArray = stateName.split('_');
        if (filePathArray.length > 1 && inheritController === true) {
          filePathArray.pop();
        } else if (filePathArray.length > 2) {
          fileName = filePathArray.pop();
          prefix = filePathArray.pop();
          fileName = prefix + fileName.charAt(0).toUpperCase() + fileName.slice(1);
          filePathArray.push(prefix);
          filePathArray.push(fileName);
        }
        if (filePathArray.length > 1) filePathArray.shift();
        dependentFiles.push('resources/' + provider.theme + '/nux/view/' + filePathArray.join('/') + 'Controller.js');
        $ocLazyLoad
          .load(dependentFiles, {
            cache: true
          })
          .then(
            function(e) {
              defer.resolve();
            },
            function(e) {
              defer.resolve();
            }
          );
        return defer.promise;
      }
      return dependentFile;
    }
    function preLoadLayoutConfig(url, stateName, module) {
      layoutConfig.$inject = ['$rootScope', '$q', '$stateParams', '$state', 'Constants', 'ajaxServiceFactory', 'storageService'];
      function layoutConfig($rootScope, $q, $stateParams, $state, Constants, ajaxServiceFactory, storageService) {
        var ajaxService,
          siteInfo,
          defer = $q.defer(),
          layoutUrl;
        if (!url && stateName) {
          url = stateName.split('_');
          url = url.slice(-2);
          url = 'layout/' + url.join('/') + '.do';
        }
        if (url) {
          siteInfo = storageService.getSiteInfo();
          layoutUrl = url;
          if ($stateParams.params && $stateParams.params.siteCode && siteInfo && siteInfo.siteCode && $stateParams.params.siteCode.substr(0, 6) != siteInfo.siteCode) {
            layoutUrl = layoutUrl + '/' + $stateParams.params.siteCode.substr(0, 6);
          }
          ajaxService = ajaxServiceFactory.buildAjaxService({
            loadConfig: {
              url: layoutUrl,
              method: 'get',
              cache: provider.cachelayout
            }
          });
          ajaxService.loadConfig().then(
            function(response) {
              defer.resolve(response ? response.data : {});
            },
            function(error) {
              log.debug('can not load config[' + layoutUrl + ']:' + error);
              defer.reject();
            }
          );
        } else {
          defer.resolve({});
        }
        return defer.promise;
      }
      return layoutConfig;
    }
    function preLoadValidatorConfig(url, stateName, module) {
      validatorConfig.$inject = ['$rootScope', '$q', '$state', 'Constants', 'storageService', 'ajaxServiceFactory'];
      function validatorConfig($rootScope, $q, $state, Constants, storageService, ajaxServiceFactory) {
        var ajaxService,
          defer = $q.defer();
        if (!url && stateName) {
          url = stateName.split('_');
          url = url.slice(-2);
          url = 'validator/' + url.join('/') + '.do';
        }
        if (url) {
          ajaxService = ajaxServiceFactory.buildAjaxService({
            loadConfig: {
              url: url,
              method: 'get',
              cache: provider.cacheValidationRule
            }
          });
          ajaxService.loadConfig().then(
            function(response) {
              var resources = response && response.data ? response.data.resources : undefined;
              var rule = response && response.data ? response.data.rules : null;
              if (resources) {
                storageService.setItemToSession(Constants.SESSION_I18N_RESOURES, resources);
                $rootScope.$emit(Constants.NOTIFY_CHANGE_LANGUAGE);
              }
              defer.resolve(rule);
            },
            function error(error) {
              log.debug('can not load config[' + url + ']:' + error);
              defer.reject();
            }
          );
        } else {
          defer.resolve({});
        }
        return defer.promise;
      }
      return validatorConfig;
    }
    function preLoadI18nConfig(url, stateName, module) {
      i18nConfig.$inject = ['$rootScope', '$q', '$state', 'Constants', 'storageService', 'ajaxServiceFactory'];
      function i18nConfig($rootScope, $q, $state, Constants, storageService, ajaxServiceFactory) {
        var ajaxService,
          defer = $q.defer();
        if (!url && stateName) {
          url = stateName.split('_');
          url = url.slice(-2);
          url = 'resource/' + url.join('/') + '.do';
        }
        if (url) {
          ajaxService = ajaxServiceFactory.buildAjaxService({
            loadConfig: {
              url: url,
              method: 'get',
              cache: provider.cacheResource
            }
          });
          ajaxService.loadConfig().then(
            function(response) {
              var resources = response ? response.data : undefined;
              if (resources) {
                storageService.setItemToSession(Constants.SESSION_I18N_RESOURES, resources);
                $rootScope.$emit(Constants.NOTIFY_CHANGE_LANGUAGE);
              }
              defer.resolve(resources);
            },
            function error(error) {
              log.debug('can not load config[' + url + ']:' + error);
              defer.reject();
            }
          );
        } else {
          defer.resolve({});
        }
        return defer.promise;
      }
      return i18nConfig;
    }
    function preLoadAuthorizationConfig(url, stateName, module) {
      authorizationConfig.$inject = ['$rootScope', '$q', '$stateParams', '$state', 'Constants', 'ajaxServiceFactory'];
      function authorizationConfig($rootScope, $q, $stateParams, $state, Constants, ajaxServiceFactory) {
        var ajaxService,
          defer = $q.defer();
        if (!url && stateName) {
          url = stateName.split('_');
          url = url.slice(-2);
          url = 'authorization/' + url.join('/') + '.do';
        }
        if (url) {
          ajaxService = ajaxServiceFactory.buildAjaxService({
            loadConfig: {
              url: url,
              method: 'get',
              cache: provider.cacheAuthorization
            }
          });
          ajaxService.loadConfig().then(
            function(response) {
              $stateParams.authorization = buildAuthService($rootScope, Constants, ajaxServiceFactory, response ? response.data : {});
              defer.resolve($stateParams.authorization);
            },
            function error(error) {
              log.debug('can not load config[' + url + ']:' + error);
              defer.reject();
            }
          );
        } else {
          defer.resolve({});
        }
        return defer.promise;
      }
      return authorizationConfig;
    }
    function buildAuthService($rootScope, Constants, ajaxServiceFactory, authorizationConfig) {
      var authorizationServices = {},
        authService,
        emptyServiceNames = [];
      angular.forEach(authorizationConfig, function(value, key) {
        if (value == '') {
          emptyServiceNames.push(key);
        } else {
          authorizationServices[key] = {
            url: value,
            method: 'post',
            cache: provider.cacheAuthorization
          };
        }
      });
      authService = ajaxServiceFactory.buildAjaxService(authorizationServices) || {};
      angular.forEach(emptyServiceNames, function(value, key) {
        authService[value] = angular.noop;
      });
      return authService;
    }
    function isLeafConfig(simpleState) {
      return (
        !simpleState ||
        (simpleState.hasOwnProperty('views') ||
          simpleState.hasOwnProperty('templateUrl') ||
          simpleState.hasOwnProperty('__multipleViews') ||
          simpleState.hasOwnProperty('__dependentFiles') ||
          simpleState.hasOwnProperty('__inheritController') ||
          simpleState.hasOwnProperty('__i18nConfig') ||
          simpleState.hasOwnProperty('__layoutConfig') ||
          simpleState.hasOwnProperty('__validatorConfig') ||
          simpleState.hasOwnProperty('__authorizationConfig'))
      );
    }
    function flattenSimpleStateConfig(simpleState) {
      var keys = {
          views: null,
          templateUrl: null,
          __multipleViews: null,
          __dependentFiles: null,
          __i18nConfig: null,
          __layoutConfig: null,
          __validatorConfig: null,
          __authorizationConfig: null,
          __inheritController: null,
          __ignore: null
        },
        config = {},
        tempConfig;
      angular.forEach(simpleState, function(value, key) {
        if (isLeafConfig(value)) {
          config[key] = value || {};
          if (value && value.__multipleViews) {
            tempConfig = flattenSimpleStateConfig(value);
            angular.forEach(tempConfig, function(v, k) {
              config[key + '.' + k] = v;
            });
          }
        } else if (!keys.hasOwnProperty(key)) {
          if (simpleState.__dependentFiles && simpleState.__dependentFiles.length > 0) {
            value.__dependentFiles = (value.__dependentFiles || []).concat(simpleState.__dependentFiles);
          }
          if (simpleState.params) {
            value.params = angular.merge(value.params || {}, simpleState.params);
          }
          tempConfig = flattenSimpleStateConfig(value);
          angular.forEach(tempConfig, function(v, k) {
            if (value.__multipleViews) {
              config[key + '.' + k] = v;
            } else {
              config[key + '_' + k] = v;
            }
          });
        }
      });
      return config;
    }
  }

  configuration.$inject = ['$locationProvider', '$urlRouterProvider', 'routerServiceProvider'];
  function configuration($locationProvider, $urlRouterProvider, routerServiceProvider) {
    // routerServiceProvider.setHomeUrl("/app/home");
    // $urlRouterProvider.deferIntercept(false);
    // $locationProvider.hashPrefix('');
    // $locationProvider.html5Mode({ enabled: false, requireBase: false, rewriteLinks: true });
  }

  main.$inject = ['$rootScope', '$state', '$stateParams', '$window', 'loggerFactory', 'storageService'];
  function main($rootScope, $state, $stateParams, $window, loggerFactory, storageService) {
    var log = loggerFactory.getLogger('router');
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
      log.debug(event.name + ': go[' + toState.name + ',' + toParams.params + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']');
    });

    $rootScope.$on('$stateNotFound', function(event, unfoundState, fromState, fromParams) {
      log.error(event.name + ': go[' + unfoundState.to + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']');
    });

    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
      var pageId = toState.name ? toState.name.split('_').join('.') : '';
      if (pageId.indexOf('poi.menu.submenu') == 0) {
        if (toParams.params && toParams.params.displayName) {
          pageId = 'poi.menu.submenu.' + toParams.params.displayName;
        }
      }

      //TODO: 缺少 wafMain.js 文件
      // $rootScope.pageId = pageId;
      // $rootScope.removePageDescription();
      // $rootScope.removeFormState();
      // $rootScope.saveAuditAndNoteParams(toState.name, toParams);
      // $rootScope.saveHistory(event, toState, toParams, fromState, fromParams);

      log.debug(event.name + ': go[' + toState.name + ',' + angular.toJson(toParams.params) + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']');
    });

    $rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
      log.error(event.name + ': go[' + toState.name + ',' + angular.toJson(toParams.params) + '], from[' + fromState.name + ',' + angular.toJson(fromParams.params) + ']: ' + error);
    });
  }
})(angular);
